/*
 * Copyright (c) 2024 endless-sky
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "record_playback_impl.h"
#include "utils_log.h"
#include <chrono>
#include <filesystem>

namespace El {
namespace Record {

PlaybackPtr IPlayback::create()
{
    return std::make_shared<CPlayback>();
}

CPlayback::CPlayback()
{
    EL_DEBUG("CPlayback constructor");
    mp4Reader_ = std::make_unique<El::Muxer::MP4Read>();
}

CPlayback::~CPlayback()
{
    stop();
    mp4Reader_.reset();
    EL_DEBUG("CPlayback destructor");
}

bool CPlayback::config(int32_t ch, uint32_t start, uint32_t end)
{
    if (ch < 0 || start >= end) {
        EL_ERROR("Invalid config parameters: ch={}, start={}, end={}", ch, start, end);
        return false;
    }
    
    std::lock_guard<std::mutex> lock(mutex_);
    channel_ = ch;
    startTime_ = start;
    endTime_ = end;
    filePath_.clear();
    
    EL_INFO("Playback configured: ch={}, start={}, end={}", ch, start, end);
    return true;
}

bool CPlayback::config(const std::string &path)
{
    if (path.empty()) {
        EL_ERROR("Invalid file path");
        return false;
    }
    
    // Check if file exists
    if (!std::filesystem::exists(path)) {
        EL_ERROR("File does not exist: {}", path);
        return false;
    }
    
    std::lock_guard<std::mutex> lock(mutex_);
    filePath_ = path;
    
    EL_INFO("Playback configured with file: {}", path);
    return true;
}

bool CPlayback::start(const Proc &proc)
{
    if (!proc) {
        EL_ERROR("Frame callback is null");
        return false;
    }
    
    std::lock_guard<std::mutex> lock(mutex_);
    if (running_) {
        EL_WARN("Playback already running");
        return false;
    }
    
    if (filePath_.empty()) {
        EL_ERROR("No file path configured");
        return false;
    }
    
    // Open MP4 file
    if (!mp4Reader_->Open(filePath_)) {
        EL_ERROR("Failed to open MP4 file: {}", filePath_);
        return false;
    }
    
    // Store the callback
    frameCallback_ = proc;
    
    // Register callbacks with MP4Reader
    mp4Reader_->RegisterVideoCallback([this](const El::Media::MediaFramePtr &frame) {
        onVideoFrame(frame);
    });
    
    mp4Reader_->RegisterAudioCallback([this](const El::Media::MediaFramePtr &frame) {
        onAudioFrame(frame);
    });
    
    // Start MP4 playback (MP4Read handles its own threading)
    if (!mp4Reader_->Start(false)) { // Don't loop by default
        EL_ERROR("Failed to start MP4 playback");
        return false;
    }
    
    running_ = true;
    paused_ = false;
    
    EL_INFO("Playback started for file: {}", filePath_);
    return true;
}

bool CPlayback::stop()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (!running_) {
        return true;
    }
    
    running_ = false;
    paused_ = false;
    
    // Stop MP4 playback
    mp4Reader_->Stop();
    
    EL_INFO("Playback stopped");
    return true;
}

bool CPlayback::pause()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (!running_) {
        EL_WARN("Playback not running");
        return false;
    }
    
    paused_ = true;
    mp4Reader_->Pause();
    EL_INFO("Playback paused");
    return true;
}

bool CPlayback::resume()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (!running_) {
        EL_WARN("Playback not running");
        return false;
    }
    
    paused_ = false;
    mp4Reader_->Resume();
    
    EL_INFO("Playback resumed");
    return true;
}

bool CPlayback::seekByTime(uint32_t time)
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (!running_) {
        EL_WARN("Playback not running");
        return false;
    }
    
    // Convert seconds to milliseconds for MP4Reader
    int64_t target_ms = static_cast<int64_t>(time) * 1000;
    
    bool success = mp4Reader_->Seek(target_ms);
    if (success) {
        EL_INFO("Seek to time: {} seconds", time);
    } else {
        EL_ERROR("Failed to seek to time: {} seconds", time);
    }
    
    return success;
}

bool CPlayback::setSpeed(float speed)
{
    if (speed <= 0) {
        EL_ERROR("Invalid speed: {}", speed);
        return false;
    }
    
    std::lock_guard<std::mutex> lock(mutex_);
    speed_ = speed;
    
    // Note: MP4Read doesn't support speed control directly
    // This would need to be implemented by adjusting frame timing in callbacks
    EL_INFO("Playback speed set to: {} (note: not fully implemented)", speed);
    return true;
}

void CPlayback::onVideoFrame(const El::Media::MediaFramePtr &frame)
{
    if (!frameCallback_ || !running_ || paused_) {
        return;
    }
    
    // Set frame properties
    frame->SetChannel(channel_);
    frame->SetFrameType(MEDIA_FRAME_I); // Default to I-frame type
    
    EL_DEBUG("Video frame callback - size: {}, pts: {}", frame->GetLength(), frame->GetPts());
    
    // Create a non-const copy for the callback
    auto mutableFrame = std::const_pointer_cast<El::Media::MediaFrame>(frame);
    frameCallback_(mutableFrame);
}

void CPlayback::onAudioFrame(const El::Media::MediaFramePtr &frame)
{
    if (!frameCallback_ || !running_ || paused_) {
        return;
    }
    
    // Set frame properties
    frame->SetChannel(channel_);
    frame->SetFrameType(MEDIA_FRAME_AAC); // Default to AAC audio frame
    
    EL_DEBUG("Audio frame callback - size: {}, pts: {}", frame->GetLength(), frame->GetPts());
    
    // Create a non-const copy for the callback
    auto mutableFrame = std::const_pointer_cast<El::Media::MediaFrame>(frame);
    frameCallback_(mutableFrame);
}

} // namespace Record
} // namespace El